/**
 * Calculate the new position for the given layout and animate the layer to this position
 */
WireIt.Layer.prototype.layoutAnim = function(sLayout, duration) {

	var layout = new WireIt.Layout[sLayout || "Spring"](this);

	var newPositions = layout.run(),
			n = this.containers.length;

	for (var i = 0; i < n; i++) {
		var c = this.containers[i],
				p = newPositions[i],
				anim = new WireIt.util.Anim(c.terminals, c.el, {  left: { to: p[0] }, top: {to: p[1]} }, duration || 1, YAHOO.util.Easing.easeOut);
		anim.animate();
	}

	delete layout;
};

/**
 * Start a dynamic layout
 */
WireIt.Layer.prototype.startDynamicLayout = function(sLayout, interval) {

	if (this.dynamicLayout) {
		this.stopDynamicLayout();
	}

	this.dynamicLayout = new WireIt.Layout[sLayout || "Spring"](this);

	var that = this;
	this.dynamicTimer = setInterval(function() {
		that._runDynamicLayout();
	}, interval || 50);

	this._runDynamicLayout();
};

WireIt.Layer.prototype._runDynamicLayout = function() {

	var newPositions = this.dynamicLayout.run(),
			n = this.containers.length;

	for (var i = 0; i < n; i++) {
		var c = this.containers[i],
				p = newPositions[i];

		// TODO: this test should be: isDragging && container focused
		if (! YAHOO.util.Dom.hasClass(c.el, "WireIt-Container-focused")) {
			c.el.style.left = p[0] + "px";
			c.el.style.top = p[1] + "px";
			c.redrawAllWires();
		}
	}

};


/**
 * Stop the dynamic layout
 */
WireIt.Layer.prototype.stopDynamicLayout = function() {
	clearInterval(this.dynamicTimer);
	this.dynamicTimer = null;
	this.dynamicLayout = null;
};


WireIt.Layout = {};


/**
 * Spring Layout
 * TODO: use different eges k
 */
WireIt.Layout.Spring = function(layer) {
	this.layer = layer;
	this.init();
};

WireIt.Layout.Spring.prototype = {

	init: function() {

		this.nodes = [];
		this.edges = [];

		// Extract wires
		for (var i = 0; i < this.layer.wires.length; i++) {
			var wire = this.layer.wires[i];
			this.edges.push([this.layer.containers.indexOf(wire.terminal1.container), this.layer.containers.indexOf(wire.terminal2.container) ]);
		}

	},

	run: function() {

		var i, j, l;

		// Extract nodes positions
		this.nodes = [];
		for (i = 0; i < this.layer.containers.length; i++) {
			var pos = this.layer.containers[i].terminals[0].getXY();

			this.nodes.push({
								layoutPosX: (pos[0] - 400) / 200,
								layoutPosY: (pos[1] - 400) / 200,
								layoutForceX: 0,
								layoutForceY: 0
							});
		}

		// Spring layout parameters
		var iterations = 100,
				maxRepulsiveForceDistance = 6,
				k = 0.4,
				c = 0.01;

		var d,dx,dy,d2,node,node1,node2;

		// Iterations
		for (l = 0; l < iterations; l++) {

			// Forces on nodes due to node-node repulsions
			for (i = 0; i < this.nodes.length; i++) {
				node1 = this.nodes[i];
				for (j = i + 1; j < this.nodes.length; j++) {
					node2 = this.nodes[j];
					dx = node2.layoutPosX - node1.layoutPosX;
					dy = node2.layoutPosY - node1.layoutPosY;
					d2 = dx * dx + dy * dy;
					if (d2 < 0.01) {
						dx = 0.1 * Math.random() + 0.1;
						dy = 0.1 * Math.random() + 0.1;
						d2 = dx * dx + dy * dy;
					}
					d = Math.sqrt(d2);
					if (d < maxRepulsiveForceDistance) {
						var repulsiveForce = k * k / d;
						node2.layoutForceX += repulsiveForce * dx / d;
						node2.layoutForceY += repulsiveForce * dy / d;
						node1.layoutForceX -= repulsiveForce * dx / d;
						node1.layoutForceY -= repulsiveForce * dy / d;
					}
				}
			}


			// Forces on this.nodes due to edge attractions
			for (i = 0; i < this.edges.length; i++) {
				var edge = this.edges[i];
				node1 = this.nodes[ edge[0] ];
				node2 = this.nodes[ edge[1] ];

				dx = node2.layoutPosX - node1.layoutPosX;
				dy = node2.layoutPosY - node1.layoutPosY;
				d2 = dx * dx + dy * dy;
				if (d2 < 0.01) {
					dx = 0.1 * Math.random() + 0.1;
					dy = 0.1 * Math.random() + 0.1;
					d2 = dx * dx + dy * dy;
				}
				d = Math.sqrt(d2);
				if (d > maxRepulsiveForceDistance) {
					d = maxRepulsiveForceDistance;
					d2 = d * d;
				}
				var attractiveForce = (d2 - k * k) / k;

				node2.layoutForceX -= attractiveForce * dx / d;
				node2.layoutForceY -= attractiveForce * dy / d;
				node1.layoutForceX += attractiveForce * dx / d;
				node1.layoutForceY += attractiveForce * dy / d;
			}

			// Move by the given force
			for (i = 0; i < this.nodes.length; i++) {
				node = this.nodes[i];
				var xmove = c * node.layoutForceX;
				var ymove = c * node.layoutForceY;

				node.layoutPosX += xmove;
				node.layoutPosY += ymove;
				node.layoutForceX = 0;
				node.layoutForceY = 0;
			}
		}

		var newPositions = [];
		for (i = 0; i < this.layer.containers.length; i++) {
			node = this.nodes[i];
			newPositions.push([node.layoutPosX * 200 + 400 - 40, node.layoutPosY * 200 + 400 - 20]);
		}
		return newPositions;

	}



};
